"""
Socket协议接口实现
"""

import socket

import threading
import asyncio
from functools import wraps
from typing import Dict, Any, Optional, List, Union, Tuple, Callable
from ....core.communication_interface import CommunicationInterface, CommunicationProtocol
from ....readwrite.typedef import *
from ....utils.logger import get_logger, default_logger
# 添加日志记录器
logger = get_logger("driver.protocols")

class SocketInterface(CommunicationInterface):
    """Socket通信接口"""

    def __init__(self, config: Dict[str, Any]):
        """
        初始化Socket接口

        Args:
            config: 配置字典
        """
        super().__init__(config)
        self.protocol = CommunicationProtocol.SOCKET
        self.host = config.get("host")
        self.port = config.get("port")
        self.socket_type = config.get("socket_type", "tcp").lower()
        self.timeout = config.get("timeout", 30)
        self.buffer_size = config.get("buffer_size", 1024)
        self.encoding = config.get("encoding", "utf-8")
        # 重连配置
        self.max_reconnect_attempts = config.get("max_reconnect_attempts", 3)
        self.reconnect_interval = config.get("reconnect_interval", 5)
        # Socket特定配置
        self.socket = None
        self.is_connected = False
        #线程安全处理
        self._client_lock = threading.RLock()  # 用于保护客户端连接操作
        self._read_lock = threading.RLock()  # 用于保护读操作
        self._write_lock = threading.RLock()  # 用于保护写操作


    def _thread_safe_method(lock_name:str)-> Callable[[Callable[..., Any]], Callable[..., Any]]:
        """装饰器：为方法提供线程安全保护，使用指定的锁"""
        def decorator(func)-> Callable[..., Any]:
            @wraps(func)
            def wrapper(self, *args, **kwargs)-> Any:
                lock = getattr(self, lock_name)
                with lock:
                    return func(self, *args, **kwargs)
            return wrapper
        return decorator

    @_thread_safe_method('_client_lock')
    def connect(self) -> bool:
        """
        建立Socket连接

        Returns:
            bool: 连接是否成功
        """
        try:
            if self.is_connected:
                logger.warning(f"Socket device {self.device_id} already connected")
                return True

            if self.socket_type == "udp":
                self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            else:
                self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            self.socket.settimeout(self.timeout)

            if self.socket_type == "tcp":
                self.socket.connect((self.host, self.port))
                logger.info(f"TCP connection established to {self.host}:{self.port}")

            self.is_connected = True

            # 通知连接回调
            for callback in self.connection_callbacks:
                try:
                    callback(True)
                except Exception as e:
                    logger.warning(f"Connection callback execution error: {e}")

            return True
        except Exception as e:
            logger.error(f"Socket connection failed for device {self.device_id}: {e}")
            self.is_connected = False

            # 通知连接回调
            for callback in self.connection_callbacks:
                try:
                    callback(False)
                except Exception as e:
                    logger.warning(f"Connection callback execution error: {e}")

            return False

    def disconnect(self) -> bool:
        """
        断开Socket连接

        Returns:
            bool: 断开是否成功
        """
        try:
            if self.socket:
                self.socket.close()
                self.socket = None

            self.is_connected = False

            # 通知连接回调
            for callback in self.connection_callbacks:
                try:
                    callback(False)
                except Exception as e:
                    logger.warning(f"Connection callback execution error: {e}")

            logger.info(f"Socket connection closed for device {self.device_id}")
            return True
        except Exception as e:
            logger.error(f"Error disconnecting socket for device {self.device_id}: {e}")
            return False

    @_thread_safe_method('_client_lock')
    def _reconnect(self) -> bool:
        """
        重新连接Socket

        Returns:
            bool: 重连是否成功
        """
        logger.info(f"Attempting to reconnect to {self.host}:{self.port} for device {self.device_id}")

        # 先断开现有连接
        self.disconnect()

        # 尝试重新连接
        for attempt in range(1, self.max_reconnect_attempts + 1):
            logger.debug(f"Reconnection attempt {attempt}/{self.max_reconnect_attempts} for device {self.device_id}")
            try:
                if self.connect():
                    logger.info(f"Successfully reconnected to {self.host}:{self.port} for device {self.device_id}")
                    return True
                else:
                    if attempt < self.max_reconnect_attempts:
                        logger.warning(f"Reconnection attempt {attempt} failed for device {self.device_id}. "
                                     f"Retrying in {self.reconnect_interval} seconds...")
                        import time
                        time.sleep(self.reconnect_interval)
            except Exception as e:
                logger.error(f"Reconnection attempt {attempt} failed with exception: {e} for device {self.device_id}")
                if attempt < self.max_reconnect_attempts:
                    logger.info(f"Retrying in {self.reconnect_interval} seconds...")
                    import time
                    time.sleep(self.reconnect_interval)

        logger.error(f"Failed to reconnect to {self.host}:{self.port} after {self.max_reconnect_attempts} attempts "
                     f"for device {self.device_id}")
        return False

    @_thread_safe_method('_client_lock')
    def is_available(self) -> bool:
        """
        检查连接是否可用,如果连接不可用，尝试自动重连

        Returns:
            bool: 连接是否可用
        """
        if not self.is_connected:
            # 如果未连接，尝试重连
            return self._reconnect()


        return True

    @_thread_safe_method('_write_lock')
    def send_data(self, data: Any, **kwargs) -> Any:
        """
        发送数据

        Args:
            data: 要发送的数据
            **kwargs: 其他参数，如目标地址(UDP)等

        Returns:
            Any: 发送结果
        """
        # 检查连接状态，如果需要则重连
        if not self.is_available() and self.socket_type == "tcp":
            raise ConnectionError(f"Socket is not connected and reconnection failed for device {self.device_id}")

        try:
            # 处理数据编码
            if isinstance(data, str):
                send_data = data.encode(self.encoding)
            elif isinstance(data, bytes):
                send_data = data
            else:
                send_data = str(data).encode(self.encoding)

            if self.socket_type == "udp":
                # UDP需要指定目标地址
                target_host = kwargs.get("target_host", self.host)
                target_port = kwargs.get("target_port", self.port)
                sent_bytes = self.socket.sendto(send_data, (target_host, target_port))
                logger.debug(f"Sent {sent_bytes} bytes via UDP to {target_host}:{target_port}")
            else:
                # TCP直接发送
                sent_bytes = self.socket.send(send_data)
                logger.debug(f"Sent {sent_bytes} bytes via TCP")

            return sent_bytes
        except Exception as e:
            logger.error(f"Error sending data for device {self.device_id}: {e}")
            # 尝试重连
            if self._reconnect():
                # 重连成功后重新发送数据
                try:
                    if isinstance(data, str):
                        send_data = data.encode(self.encoding)
                    elif isinstance(data, bytes):
                        send_data = data
                    else:
                        send_data = str(data).encode(self.encoding)

                    if self.socket_type == "udp":
                        target_host = kwargs.get("target_host", self.host)
                        target_port = kwargs.get("target_port", self.port)
                        sent_bytes = self.socket.sendto(send_data, (target_host, target_port))
                        logger.debug(f"Sent {sent_bytes} bytes via UDP to {target_host}:{target_port} after reconnection")
                    else:
                        sent_bytes = self.socket.send(send_data)
                        logger.debug(f"Sent {sent_bytes} bytes via TCP after reconnection")
                    return sent_bytes
                except Exception as re_send_error:
                    logger.error(f"Failed to send data after reconnection for device {self.device_id}: {re_send_error}")
                    raise
            raise

    @_thread_safe_method('_read_lock')
    def receive_data(self, timeout: Optional[float] = None) -> Any:
        """
        接收数据

        Args:
            timeout: 超时时间

        Returns:
            Any: 接收到的数据
        """
        # 检查连接状态，如果需要则重连
        if not self.is_available() and self.socket_type == "tcp":
            raise ConnectionError(f"Socket is not connected and reconnection failed for device {self.device_id}")

        try:
            # 设置临时超时时间
            if timeout is not None:
                original_timeout = self.socket.gettimeout()
                self.socket.settimeout(timeout)

            try:
                if self.socket_type == "udp":
                    # UDP接收数据和地址
                    data, addr = self.socket.recvfrom(self.buffer_size)
                    logger.debug(f"Received {len(data)} bytes via UDP from {addr}")
                else:
                    # TCP接收数据
                    data = self.socket.recv(self.buffer_size)
                    logger.debug(f"Received {len(data)} bytes via TCP")

                # 根据编码解码数据
                if data:
                    try:
                        return data.decode(self.encoding)
                    except UnicodeDecodeError:
                        # 如果无法解码为字符串，则返回原始字节
                        return data
                else:
                    return None
            finally:
                # 恢复原始超时时间
                if timeout is not None:
                    self.socket.settimeout(original_timeout)

        except socket.timeout:
            logger.debug(f"Socket receive timeout for device {self.device_id}")
            raise
        except Exception as e:
            logger.error(f"Error receiving data for device {self.device_id}: {e}")
            # 尝试重连
            if self._reconnect():
                # 重连成功后重新尝试接收数据
                try:
                    if timeout is not None:
                        original_timeout = self.socket.gettimeout()
                        self.socket.settimeout(timeout)

                    if self.socket_type == "udp":
                        data, addr = self.socket.recvfrom(self.buffer_size)
                        logger.debug(f"Received {len(data)} bytes via UDP from {addr} after reconnection")
                    else:
                        data = self.socket.recv(self.buffer_size)
                        logger.debug(f"Received {len(data)} bytes via TCP after reconnection")

                    if timeout is not None:
                        self.socket.settimeout(original_timeout)

                    if data:
                        try:
                            return data.decode(self.encoding)
                        except UnicodeDecodeError:
                            return data
                    else:
                        return None
                except Exception as re_recv_error:
                    logger.error(f"Failed to receive data after reconnection for device {self.device_id}: {re_recv_error}")
                    raise
            raise

    @_thread_safe_method('_write_lock')
    def execute_command(self, command: str, params: Optional[Dict] = None) -> Any:
        """
        执行命令,发送命令并等待响应

        Args:
            command: 命令字符串
            params: 命令参数

        Returns:
            Any: 命令执行结果
        """
        # 检查连接状态，如果需要则重连
        if not self.is_available() and self.socket_type == "tcp":
            raise ConnectionError(f"Socket is not connected and reconnection failed for device {self.device_id}")

        try:
            # 构造命令消息
            if params:
                message = f"{command}:{params}"
            else:
                message = command

            # 发送命令
            self.send_data(message)
            logger.debug(f"Command '{command}' sent to {self.device_id}")

            # 接收响应
            response = self.receive_data(timeout= 5.0)
            logger.debug(f"Response received from {self.device_id}: {response}")
            return response
        except Exception as e:
            logger.error(f"Error executing command '{command}' for device {self.device_id}: {e}")
            raise


    async def async_send_data(self, data: Any, **kwargs) -> Any:
        """
        异步发送数据

        Args:
            data: 要发送的数据
            **kwargs: 其他参数，如目标地址(UDP)等

        Returns:
            Any: 发送结果
        """
        # 使用asyncio的执行器在单独的线程中运行阻塞的发送操作
        loop = asyncio.get_running_loop()
        return await loop.run_in_executor(
            None,           # 默认线程池
            self.send_data, # 目标函数
            data,           # 位置参数
            *kwargs.items() # 如果 kwargs 需要展开；或直接传给 send_data
        )

    async def async_receive_data(self, timeout: Optional[float] = None) -> Any:
        """
        异步接收数据

        Args:
            timeout: 超时时间

        Returns:
            Any: 接收到的数据
        """
        try:

            # 使用asyncio的执行器在单独的线程中运行阻塞的接收操作
            loop = asyncio.get_running_loop()  # 更安全的获取当前运行 loop 的方式
            return await loop.run_in_executor(
                None,  # 使用默认线程池
                self.receive_data, # 调用同步的 receive_data 方法
                timeout
            )
        except Exception as e:
            logger.error(f"Error in async_receive_data for device {self.device_id}: {e}")
            raise


